using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lichtspiel.Math;

namespace Lichtspiel.Input
{
    public class Mouse : IUpdateable
    {
        private const int NumOfButtons = 3;
        private bool[] buttonState;
        private bool[] lastButtonState;
        private bool[] internalButtonState;

        private int lastX;
        private int lastY;

        // todo lastx, moveevent
        private OpenTK.Input.MouseDevice mouseDevice;


        public event EventHandler<MouseButtonEventArgs> ButtonPressed;
        public event EventHandler<MouseButtonEventArgs> ButtonReleased;
        public event EventHandler<MouseMovedEventArgs> MouseMoved;

        private bool isShowing = true;
        public bool IsShowing {
            get { return isShowing; }

            set { 
                isShowing = value;
                if (value)
                    System.Windows.Forms.Cursor.Show();
                else
                    System.Windows.Forms.Cursor.Hide();
            }
        }

        public int X {
            get; private set;
        }

        public int Y {
            get;
            private set;
        }

        public int DeltaX {
            get { return X - lastX; }
        }

        public int DeltaY {
            get { return Y - lastY; }
        }

        public Vector2 Position {
            get { return new Vector2(X,Y);}
        }

        public Vector2 Delta {
            get { return new Vector2(DeltaX, DeltaY);}
        }

        public Mouse(Game game) {            
            mouseDevice = game.window.Mouse;

            mouseDevice.ButtonDown += new EventHandler<OpenTK.Input.MouseButtonEventArgs>(Mouse_ButtonDown);
            mouseDevice.ButtonUp += new EventHandler<OpenTK.Input.MouseButtonEventArgs>(Mouse_ButtonUp);

            buttonState = new bool[NumOfButtons];
            lastButtonState = new bool[NumOfButtons];
            internalButtonState = new bool[NumOfButtons];

            IsShowing = IsShowing; // active default
            UpdateMousePosition();
        }

        void Mouse_ButtonUp(object sender, OpenTK.Input.MouseButtonEventArgs e) {
            if (((int)e.Button) >= NumOfButtons)
                return;

            internalButtonState[(int)e.Button] = false;
        }

        void Mouse_ButtonDown(object sender, OpenTK.Input.MouseButtonEventArgs e) {
            if (((int)e.Button) >= NumOfButtons)
                return;

            internalButtonState[(int)e.Button] = true;
        }

        public bool IsButtonDown(MouseButton button) {
            return this.buttonState[(int) button];
        }

        public bool IsButtonUp(MouseButton button) {
            return !this.buttonState[(int)button];
        }

        public bool IsButtonPressed(MouseButton button) {
            return this.buttonState[(int)button] && !this.lastButtonState[(int)button];
        }

        public bool IsButtonReleased(MouseButton button) {
            return !this.buttonState[(int)button] && this.lastButtonState[(int)button];
        }

        public bool IsMoved() {
            return this.DeltaX != 0 || this.DeltaY != 0;
        }

        public void ShowCursor() {
            if (!IsShowing)
                IsShowing = true;
        }

        public void HideCursor() {
            if (IsShowing)
                IsShowing = false;
        }

        public void Update(GameTime time) {
            buttonState.CopyTo(lastButtonState, 0);
            internalButtonState.CopyTo(buttonState, 0);

            UpdateMousePosition();

            if (this.IsMoved()) {
                FireMouseMovedEvent(new MouseMovedEventArgs(this.X, this.Y, this.DeltaX, this.DeltaY));
            }

            for (int i = 0; i < buttonState.Length; i++) {
                if (IsButtonPressed((MouseButton)i))
                    FireButtonPressedEvent(new MouseButtonEventArgs(this.X, this.Y, (MouseButton)i));
                else if (IsButtonReleased((MouseButton)i))
                    FireButtonReleasedEvent(new MouseButtonEventArgs(this.X, this.Y, (MouseButton)i));
            }                        
        }

        private void UpdateMousePosition() {
            lastX = this.X;
            lastY = this.Y;
            this.X = mouseDevice.X;
            this.Y = mouseDevice.Y;
        }

        private void FireMouseMovedEvent(MouseMovedEventArgs args) {
            if (MouseMoved != null) {
                MouseMoved(this, args);
            }
        }

        public void FireButtonPressedEvent(MouseButtonEventArgs args) {
            if (ButtonPressed != null) {
                ButtonPressed(this, args);
            }
        }

        public void FireButtonReleasedEvent(MouseButtonEventArgs args) {
            if (ButtonReleased != null) {
                ButtonReleased(this, args);
            }
        }


    }
}
